﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;

using GraphicsFramework.Entity;
using GraphicsFramework.Property;

using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;

namespace CanvasOpenGL.Graphics
{
    class OpenGL : GraphicsFramework.GraphicsFramework
    {

        public OpenGL(int width, int height, int gridsize, GraphicsFramework.Entity.Point2F gridvalue)
            : base(width, height, gridsize, gridvalue)
        {
        }

        public override void SetGraphics(object graphics)
        {
        }

        public override void Resize(int width, int height)
        {
            base.Resize(width, height);
            UpdateViewport();
        }

        public void UpdateViewport()
        {
            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadIdentity();
            GL.Ortho(0, this.Width, this.Height, 0, -1, 1);
            GL.Viewport(0, 0, this.Width, this.Height);
            GL.MatrixMode(MatrixMode.Modelview);
        }

        public override GraphicsFramework.Enum.EnumKeys GetKey(object e)
        {
            KeyEventArgs key = (KeyEventArgs)e;

            switch (key.KeyCode)
            {
                //Numeric
                case Keys.D0:
                case Keys.NumPad0:
                    return GraphicsFramework.Enum.EnumKeys.Num0;
                case Keys.D1:
                case Keys.NumPad1:
                case Keys.Oem1:
                    return GraphicsFramework.Enum.EnumKeys.Num1;
                case Keys.D2:
                case Keys.NumPad2:
                case Keys.Oem2:
                    return GraphicsFramework.Enum.EnumKeys.Num2;
                case Keys.D3:
                case Keys.NumPad3:
                case Keys.Oem3:
                    return GraphicsFramework.Enum.EnumKeys.Num3;
                case Keys.D4:
                case Keys.NumPad4:
                case Keys.Oem4:
                    return GraphicsFramework.Enum.EnumKeys.Num4;
                case Keys.D5:
                case Keys.NumPad5:
                case Keys.Oem5:
                    return GraphicsFramework.Enum.EnumKeys.Num5;
                case Keys.D6:
                case Keys.NumPad6:
                case Keys.Oem6:
                    return GraphicsFramework.Enum.EnumKeys.Num6;
                case Keys.D7:
                case Keys.NumPad7:
                case Keys.Oem7:
                    return GraphicsFramework.Enum.EnumKeys.Num7;
                case Keys.D8:
                case Keys.NumPad8:
                case Keys.Oem8:
                    return GraphicsFramework.Enum.EnumKeys.Num8;
                case Keys.D9:
                case Keys.NumPad9:
                    return GraphicsFramework.Enum.EnumKeys.Num9;

                //Alphabets
                case Keys.A:
                    return GraphicsFramework.Enum.EnumKeys.A;
                case Keys.B:
                    return GraphicsFramework.Enum.EnumKeys.B;
                case Keys.C:
                    return GraphicsFramework.Enum.EnumKeys.C;
                case Keys.D:
                    return GraphicsFramework.Enum.EnumKeys.D;
                case Keys.E:
                    return GraphicsFramework.Enum.EnumKeys.E;
                case Keys.F:
                    return GraphicsFramework.Enum.EnumKeys.F;
                case Keys.G:
                    return GraphicsFramework.Enum.EnumKeys.G;
                case Keys.H:
                    return GraphicsFramework.Enum.EnumKeys.H;
                case Keys.I:
                    return GraphicsFramework.Enum.EnumKeys.I;
                case Keys.J:
                    return GraphicsFramework.Enum.EnumKeys.J;
                case Keys.K:
                    return GraphicsFramework.Enum.EnumKeys.K;
                case Keys.L:
                    return GraphicsFramework.Enum.EnumKeys.L;
                case Keys.M:
                    return GraphicsFramework.Enum.EnumKeys.M;
                case Keys.N:
                    return GraphicsFramework.Enum.EnumKeys.N;
                case Keys.O:
                    return GraphicsFramework.Enum.EnumKeys.O;
                case Keys.P:
                    return GraphicsFramework.Enum.EnumKeys.P;
                case Keys.Q:
                    return GraphicsFramework.Enum.EnumKeys.Q;
                case Keys.R:
                    return GraphicsFramework.Enum.EnumKeys.R;
                case Keys.S:
                    return GraphicsFramework.Enum.EnumKeys.S;
                case Keys.T:
                    return GraphicsFramework.Enum.EnumKeys.T;
                case Keys.U:
                    return GraphicsFramework.Enum.EnumKeys.U;
                case Keys.V:
                    return GraphicsFramework.Enum.EnumKeys.V;
                case Keys.W:
                    return GraphicsFramework.Enum.EnumKeys.W;
                case Keys.X:
                    return GraphicsFramework.Enum.EnumKeys.X;
                case Keys.Y:
                    return GraphicsFramework.Enum.EnumKeys.Y;
                case Keys.Z:
                    return GraphicsFramework.Enum.EnumKeys.Z;

                //Named Keys
                case Keys.Enter:
                    return GraphicsFramework.Enum.EnumKeys.Enter;
                case Keys.Tab:
                    return GraphicsFramework.Enum.EnumKeys.Tab;
                case Keys.Space:
                    return GraphicsFramework.Enum.EnumKeys.Space;
                case Keys.Escape:
                    return GraphicsFramework.Enum.EnumKeys.Escape;
                case Keys.PrintScreen:
                    return GraphicsFramework.Enum.EnumKeys.PrintScreen;
                case Keys.Scroll:
                    return GraphicsFramework.Enum.EnumKeys.ScrollLock;
                case Keys.Pause:
                    return GraphicsFramework.Enum.EnumKeys.Pause;
                case Keys.Insert:
                    return GraphicsFramework.Enum.EnumKeys.Insert;
                case Keys.Delete:
                    return GraphicsFramework.Enum.EnumKeys.Delete;
                case Keys.Home:
                    return GraphicsFramework.Enum.EnumKeys.Home;
                case Keys.End:
                    return GraphicsFramework.Enum.EnumKeys.End;
                case Keys.PageUp:
                    return GraphicsFramework.Enum.EnumKeys.PageUp;
                case Keys.PageDown:
                    return GraphicsFramework.Enum.EnumKeys.PageDown;

                //+, -, *, -
                case Keys.Add:
                    return GraphicsFramework.Enum.EnumKeys.Add;
                case Keys.Oemplus:
                    return GraphicsFramework.Enum.EnumKeys.Add;
                case Keys.Subtract:
                    return GraphicsFramework.Enum.EnumKeys.Subtract;
                case Keys.OemMinus:
                    return GraphicsFramework.Enum.EnumKeys.Subtract;

                //Arrows
                case Keys.Left:
                    return GraphicsFramework.Enum.EnumKeys.Left;
                case Keys.Right:
                    return GraphicsFramework.Enum.EnumKeys.Right;
                case Keys.Up:
                    return GraphicsFramework.Enum.EnumKeys.Up;
                case Keys.Down:
                    return GraphicsFramework.Enum.EnumKeys.Down;

                //Functions
                case Keys.F1:
                    return GraphicsFramework.Enum.EnumKeys.F1;
                case Keys.F2:
                    return GraphicsFramework.Enum.EnumKeys.F2;
                case Keys.F3:
                    return GraphicsFramework.Enum.EnumKeys.F3;
                case Keys.F4:
                    return GraphicsFramework.Enum.EnumKeys.F4;
                case Keys.F5:
                    return GraphicsFramework.Enum.EnumKeys.F5;
                case Keys.F6:
                    return GraphicsFramework.Enum.EnumKeys.F6;
                case Keys.F7:
                    return GraphicsFramework.Enum.EnumKeys.F7;
                case Keys.F8:
                    return GraphicsFramework.Enum.EnumKeys.F8;
                case Keys.F9:
                    return GraphicsFramework.Enum.EnumKeys.F9;
                case Keys.F10:
                    return GraphicsFramework.Enum.EnumKeys.F10;
                case Keys.F11:
                    return GraphicsFramework.Enum.EnumKeys.F11;
                case Keys.F12:
                    return GraphicsFramework.Enum.EnumKeys.F12;
                case Keys.F13:
                    return GraphicsFramework.Enum.EnumKeys.F13;
                case Keys.F14:
                    return GraphicsFramework.Enum.EnumKeys.F14;
                case Keys.F15:
                    return GraphicsFramework.Enum.EnumKeys.F15;
                case Keys.F16:
                    return GraphicsFramework.Enum.EnumKeys.F16;
                case Keys.F17:
                    return GraphicsFramework.Enum.EnumKeys.F17;
                case Keys.F18:
                    return GraphicsFramework.Enum.EnumKeys.F18;
                case Keys.F19:
                    return GraphicsFramework.Enum.EnumKeys.F19;
                case Keys.F20:
                    return GraphicsFramework.Enum.EnumKeys.F20;
                case Keys.F21:
                    return GraphicsFramework.Enum.EnumKeys.F21;
                case Keys.F22:
                    return GraphicsFramework.Enum.EnumKeys.F22;
                case Keys.F23:
                    return GraphicsFramework.Enum.EnumKeys.F23;
                case Keys.F24:
                    return GraphicsFramework.Enum.EnumKeys.F24;

                //Default
                default:
                    return GraphicsFramework.Enum.EnumKeys.None;
            }

        }

        public override GraphicsFramework.Enum.EnumMouseButtons GetMouseButton(object e)
        {
            MouseEventArgs mouse = (MouseEventArgs)e;

            switch (mouse.Button)
            {
                case MouseButtons.Left:
                    return GraphicsFramework.Enum.EnumMouseButtons.LEFT;
                case MouseButtons.Middle:
                    return GraphicsFramework.Enum.EnumMouseButtons.MIDDLE;
                case MouseButtons.Right:
                    return GraphicsFramework.Enum.EnumMouseButtons.RIGHT;
            }

            return GraphicsFramework.Enum.EnumMouseButtons.NONE;
        }

        private Point[] GetPoints(IList<Point2F> points)
        {
            Point[] cpoint = new Point[points.Count];

            for (int count = 0; count < points.Count; count++)
                cpoint[count] = new Point(ToCoordX(points[count].X), ToCoordY(points[count].Y));

            return cpoint;
        }

        public override void BasicLine(DrawProperty property, Point2I point1, Point2I point2)
        {
            GL.Color3(property.LineColor.Red, property.LineColor.Green, property.LineColor.Blue);
            GL.LineWidth(GetLineWidth(property));

            GL.Begin(BeginMode.Lines);
            GL.Vertex3(point1.X, point1.Y, 0);
            GL.Vertex3(point2.X, point2.Y, 0);
            GL.End();
        }

        public override void DrawLine(DrawProperty property, Point2F point1, Point2F point2)
        {
            GL.Color3(property.LineColor.Red, property.LineColor.Green, property.LineColor.Blue);
            GL.LineWidth(GetLineWidth(property));
            Point Point1 = new Point(ToCoordX(point1.X), ToCoordY(point1.Y));
            Point Point2 = new Point(ToCoordX(point2.X), ToCoordY(point2.Y));

            GL.Begin(BeginMode.Lines);
            GL.Vertex3(Point1.X, Point1.Y, 0);
            GL.Vertex3(Point2.X, Point2.Y, 0);
            GL.End();
        }

        public override void DrawCircle(DrawProperty property, Point2F point1, float radius)
        {
            GL.Color4(property.LineColor.Red, property.LineColor.Green, property.LineColor.Blue, property.LineColor.Alpha);
            GL.LineWidth(GetLineWidth(property));

            int segments = 8;
            float theta = (float)(2 * Math.PI / segments);
            float tangetial_factor = (float)Math.Tan(theta);//calculate the tangential factor 

            float radial_factor = (float)Math.Cos(theta);//calculate the radial factor 
            float x = radius * Gridsize; //we start at angle = 0 
            float y = 0;

            GL.Begin(BeginMode.LineLoop);
            for (int ii = 0; ii < segments; ii++)
            {
                GL.Vertex2(x + point1.X, y + point1.Y);//output vertex 

                //add the tangential vector 

                x += -y * tangetial_factor;
                y += x * tangetial_factor;

                //correct using the radial factor 

                x *= radial_factor;
                y *= radial_factor;
            }

            GL.End();

        }

        public override void BasicEllipse(DrawProperty property, Point2I point, int width, int height)
        {
            throw new NotImplementedException();
        }

        public override void BasicEllipse(DrawProperty property, Point2I point1, Point2I point2)
        {
            throw new NotImplementedException();
        }

        public override void DrawEllipse(DrawProperty property, Point2F point1, float width, float height)
        {
            throw new NotImplementedException();
        }

        public override void DrawEllipse(DrawProperty property, Point2F point1, Point2F point2)
        {
            throw new NotImplementedException();
        }

        #region Rectangle

        public override void BasicRectangle(DrawProperty property, Point2I point, int width, int height)
        {

            float hwidth = width / 2;
            float hheight = height / 2;

            float px1 = point.X - hwidth;
            float px2 = point.X + hwidth;

            float py1 = point.Y - hheight;
            float py2 = point.Y + hheight;

            if (property.ShowFill)
            {
                GL.Color4(property.FillColor.Red, property.FillColor.Green, property.FillColor.Blue, property.FillColor.Alpha);
                GL.Begin(BeginMode.Polygon);
                GL.Vertex3(px1, py1, 0);
                GL.Vertex3(px1, py2, 0);
                GL.Vertex3(px2, py2, 0);
                GL.Vertex3(px2, py1, 0);
                GL.End();
            }

            if (property.ShowOutline)
            {
                GL.Color4(property.LineColor.Red, property.LineColor.Green, property.LineColor.Blue, property.LineColor.Alpha);
                GL.LineWidth(GetLineWidth(property));

                GL.Begin(BeginMode.LineLoop);
                GL.Vertex3(px1, py1, 0);
                GL.Vertex3(px1, py2, 0);
                GL.Vertex3(px2, py2, 0);
                GL.Vertex3(px2, py1, 0);
                GL.End();
            }
        }

        public override void BasicRectangle(DrawProperty property, Point2F point, float width, float height)
        {
        
            float hwidth = width / 2;
            float hheight = height / 2;

            float px1 = point.X - hwidth;
            float px2 = point.X + hwidth;

            float py1 = point.Y - hheight;
            float py2 = point.Y + hheight;

            if (property.ShowFill)
            {
                GL.Color4(property.FillColor.Red, property.FillColor.Green, property.FillColor.Blue, property.FillColor.Alpha);
                GL.Begin(BeginMode.Polygon);
                GL.Vertex3(px1, py1, 0);
                GL.Vertex3(px1, py2, 0);
                GL.Vertex3(px2, py2, 0);
                GL.Vertex3(px2, py1, 0);
                GL.End();
            }

            if (property.ShowOutline)
            {
                GL.Color4(property.LineColor.Red, property.LineColor.Green, property.LineColor.Blue, property.LineColor.Alpha);
                GL.LineWidth(GetLineWidth(property));

                GL.Begin(BeginMode.LineLoop);
                GL.Vertex3(px1, py1, 0);
                GL.Vertex3(px1, py2, 0);
                GL.Vertex3(px2, py2, 0);
                GL.Vertex3(px2, py1, 0);
                GL.End();
            }
        }

        public override void DrawRectangle(DrawProperty property, Point2I point, int width, int height)
        {
            Point Coord = new Point(ToCoordX(point.X), ToCoordY(point.Y));

            width *= Gridsize;
            height *= Gridsize;

            float hwidth = width / 2;
            float hheight = height / 2;

            float px1 = Coord.X - hwidth;
            float px2 = Coord.X + hwidth;

            float py1 = Coord.Y - hheight;
            float py2 = Coord.Y + hheight;

            if (property.ShowFill)
            {
                GL.Color4(property.LineColor.Red, property.LineColor.Green, property.LineColor.Blue, property.LineColor.Alpha);
                GL.Begin(BeginMode.Quads);
                GL.Vertex3(px1, py1, 0);
                GL.Vertex3(px1, py2, 0);
                GL.Vertex3(px2, py2, 0);
                GL.Vertex3(px2, py1, 0);
                GL.End();
            }

            if (property.ShowOutline)
            {
                GL.Color4(property.LineColor.Red, property.LineColor.Green, property.LineColor.Blue, property.LineColor.Alpha);
                GL.LineWidth(GetLineWidth(property));

                GL.Begin(BeginMode.LineLoop);
                GL.Vertex3(px1, py1, 0);
                GL.Vertex3(px1, py2, 0);
                GL.Vertex3(px2, py2, 0);
                GL.Vertex3(px2, py1, 0);
                GL.End();
            }
        }

        public override void DrawRectangle(DrawProperty property, Point2F point, float width, float height)
        {
        
            Point Coord = new Point(ToCoordX(point.X), ToCoordY(point.Y));

            width *= Gridsize;
            height *= Gridsize;

            float hwidth = width / 2;
            float hheight = height / 2;

            float px1 = Coord.X - hwidth;
            float px2 = Coord.X + hwidth;

            float py1 = Coord.Y - hheight;
            float py2 = Coord.Y + hheight;

            if (property.ShowFill)
            {
                GL.Color4(property.LineColor.Red, property.LineColor.Green, property.LineColor.Blue, property.LineColor.Alpha);
                GL.Begin(BeginMode.Quads);
                GL.Vertex3(px1, py1, 0);
                GL.Vertex3(px1, py2, 0);
                GL.Vertex3(px2, py2, 0);
                GL.Vertex3(px2, py1, 0);
                GL.End();
            }

            if (property.ShowOutline)
            {
                GL.Color4(property.LineColor.Red, property.LineColor.Green, property.LineColor.Blue, property.LineColor.Alpha);
                GL.LineWidth(GetLineWidth(property));

                GL.Begin(BeginMode.LineLoop);
                GL.Vertex3(px1, py1, 0);
                GL.Vertex3(px1, py2, 0);
                GL.Vertex3(px2, py2, 0);
                GL.Vertex3(px2, py1, 0);
                GL.End();
            }
        }
        
        #endregion

        #region Polyline
       
        public override void DrawPolyline(DrawProperty property, IList<Point2F> points)
        {
            //Convert to device points
            Point[] cpoint = GetPoints(points);

            GL.Color4(property.LineColor.Red, property.LineColor.Green, property.LineColor.Blue, property.LineColor.Alpha);
            GL.LineWidth(GetLineWidth(property));
            GL.Begin(BeginMode.LineStrip);

            foreach (Point point in cpoint)
            {
                GL.Vertex3(point.X, point.Y, 0);
            }

            GL.End();
        }

        #endregion

        #region Polygon

        public override void DrawPolygon(DrawProperty property, IList<Point2F> points)
        {
            Point[] cpoint = GetPoints(points);

            if (property.ShowFill)
            {
                GL.Color4(property.FillColor.Red, property.FillColor.Green, property.FillColor.Blue, property.FillColor.Alpha);
                GL.Begin(BeginMode.Polygon);

                foreach (Point point in cpoint)
                {
                    GL.Vertex3(point.X, point.Y, 0);
                    GL.Vertex3(point.X, point.Y, 0);
                }

                GL.End();
            }

            if (property.ShowOutline)
            {
                GL.Color4(property.LineColor.Red, property.LineColor.Green, property.LineColor.Blue, property.LineColor.Alpha);
                GL.LineWidth(GetLineWidth(property));
                GL.Begin(BeginMode.LineLoop);

                foreach (Point point in cpoint)
                {
                    GL.Vertex3(point.X, point.Y, 0);
                    GL.Vertex3(point.X, point.Y, 0);
                }

                GL.End();
            }
        }
        
        #endregion

        #region String

        public override void DrawText(DrawProperty property, TextProperty textproperty, Point2F point, string text)
        {
        }

        public override Point2I MeasureString(TextProperty textproperty, string text)
        {
            return new Point2I();
        }

        #endregion


        public override void BasicCircle(DrawProperty property, Point2I point, float radius)
        {
            throw new NotImplementedException();
        }

        public override void BasicCircle(DrawProperty property, Point2F point, float radius)
        {
            throw new NotImplementedException();
        }
    }
}
